package com.cl.ks.aspect;

import com.alibaba.fastjson.JSONObject;
import com.cl.spark.dto.SparkResult;
import com.cl.ks.entity.KsFilter;
import com.cl.ks.entity.KsFilterLog;
import com.cl.ks.entity.KsFilterStatistics;
import com.cl.ks.entity.KsRule;
import com.cl.ks.repository.KsFilterLogRepository;
import com.cl.ks.repository.KsFilterStatisticsRepository;
import com.cl.ks.service.FilterService;
import com.cl.ks.service.KsRuleServiceOld;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.transaction.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static com.cl.ks.constant.QTableConstant.*;


@Aspect
@Component
@Slf4j
public class KsAspect {

    private final JPAQueryFactory queryFactory;

    private final KsFilterStatisticsRepository ksFilterStatisticsRepository;

    private final KsFilterLogRepository ksFilterLogRepository;

    public KsAspect(JPAQueryFactory queryFactory, KsFilterStatisticsRepository ksFilterStatisticsRepository, KsFilterLogRepository ksFilterLogRepository) {
        this.queryFactory = queryFactory;
        this.ksFilterStatisticsRepository = ksFilterStatisticsRepository;
        this.ksFilterLogRepository = ksFilterLogRepository;
    }

    @Pointcut("execution(public * com.cl.ks.service.FilterAsyncService.filterAsync(..))")
    public void filterLog() {
    }//签名，可以理解成这个切入点的一个名称

    @Pointcut("execution(public * com.cl.ks.service.FilterAsyncService.filterSync(..))")
    public void filterTaskLog() {
    }//签名，可以理解成这个切入点的一个名称

    @Pointcut("execution(public * com.cl.ks.service.KsRuleServiceOld.filterData(..))")
    public void filterDataApiLog() {
    }//签名，可以理解成这个切入点的一个名称

    @Pointcut("execution(public * com.cl.ks.flow.util.RuleUtil.handleRule(..))")
    public void handleRuleNodeLog() {
    }

    @Around("filterDataApiLog()")
    @Transactional
    public Object filterDataApiLog(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        JSONObject jsonResult = ((ResponseEntity<JSONObject>) result).getBody();
        KsRuleServiceOld.FilterDataParam filterDataParam = (KsRuleServiceOld.FilterDataParam) joinPoint.getArgs()[0];
        Integer ruleIdParam = filterDataParam.getRuleId();
        List<Integer> ruleIdList = new ArrayList<>();
        if (ruleIdParam != null) {
            ruleIdList.add(ruleIdParam);
        }
        if (filterDataParam.getTableName() != null) {
            ruleIdList = queryFactory.select(qRlRuleTable.ruleId).from(qRlRuleTable).where(qRlRuleTable.tableName.eq(filterDataParam.getTableName())).fetch();
        }

        Integer match = Objects.requireNonNull(jsonResult).getBoolean("match") ? 1 : 0;

        ruleIdList.forEach(ruleId -> {
            KsFilterStatistics ksFilterStatistics = queryFactory.selectFrom(qKsFilterStatistics).where(qKsFilterStatistics.ruleId.eq(ruleId)).fetchFirst();
            if (ksFilterStatistics == null) {
                ksFilterStatistics = new KsFilterStatistics();
            }
            ksFilterStatistics.setRuleId(ruleId);
            ksFilterStatistics.setRunCount(ksFilterStatistics.getRunCount() + 1);
            ksFilterStatistics.setClueResultTotal(ksFilterStatistics.getClueResultTotal() + match);
            ksFilterStatistics.setRunDataTotal(ksFilterStatistics.getRunDataTotal() + match);
            ksFilterStatistics.setRunLastTime(LocalDateTime.now());

            ksFilterStatisticsRepository.deleteByRuleId(ruleId);
            ksFilterStatisticsRepository.save(ksFilterStatistics);
        });


        return result;
    }

    @After("filterLog()") //在切入点的方法run之前要干的
    public void logAfterFilter(JoinPoint joinPoint) {
        filterLogHandler(joinPoint, false);
    }

    @After("filterTaskLog()") //在切入点的方法run之前要干的
    public void logAfterFilterTask(JoinPoint joinPoint) {
        filterLogHandler(joinPoint, true);
    }

    @Around("handleRuleNodeLog()")
    public Object handleRuleNodeLogAround(ProceedingJoinPoint joinPoint) throws Throwable {
        SparkResult result = null;
        try {

            result = (SparkResult) joinPoint.proceed();
            KsRule ksRule = (KsRule) joinPoint.getArgs()[0];
            Integer ruleId = ksRule.getId();
            SparkResult param = (SparkResult) joinPoint.getArgs()[1];

            KsFilterLog ksFilterLog = new KsFilterLog();
            ksFilterLog.setRuleId(ruleId);
            ksFilterLog.setKsMatch(Math.toIntExact(result.getCount()));
            ksFilterLog.setKsNoMatch((int) (param.getCount() - result.getCount()));
            ksFilterLog.setSourceType("手动执行");
            ksFilterLog.setCreateTime(LocalDateTime.now());
            ksFilterLogRepository.save(ksFilterLog);

            KsFilterStatistics ksFilterStatistics = queryFactory.selectFrom(qKsFilterStatistics).where(qKsFilterStatistics.ruleId.eq(ruleId)).fetchFirst();
//        Long clueResultTotal = queryFactory.select(qKsFlowRuleResult.count()).from(qKsFlowRuleResult).where(qKsFlowRuleResult.ruleId.eq(ruleId)).fetchOne();
            if (ksFilterStatistics == null) {
                ksFilterStatistics = new KsFilterStatistics();
            }
            ksFilterStatistics.setRuleId(ruleId);
            ksFilterStatistics.setRunCount(ksFilterStatistics.getRunCount() + 1);
//        ksFilterStatistics.setClueResultTotal(clueResultTotal.intValue());
            ksFilterStatistics.setRunDataTotal(Math.toIntExact(param.getCount()));
            ksFilterStatistics.setRunLastTime(LocalDateTime.now());
            ksFilterStatisticsRepository.save(ksFilterStatistics);

            return result;
        } catch (Exception e) {
            throw e.getCause();
        }

    }

    @Transactional
    public void filterLogHandler(JoinPoint joinPoint, Boolean isTask) {
        Integer filterId = (Integer) joinPoint.getArgs()[0];
        FilterService.FilterBundle filterBundle = (FilterService.FilterBundle) joinPoint.getArgs()[1];
        List<FilterService.FilterParam> filterParams = filterBundle.getFilterParams();
        KsFilter filter = queryFactory.selectFrom(qKsFilter).where(qKsFilter.id.eq(filterId)).fetchFirst();
        assert filter != null;
        filterParams.forEach(filterParam -> {
            KsFilterLog ksFilterLog = new KsFilterLog();
            ksFilterLog.setFilterId(filterId);
            Integer ruleId = filterParam.getKsRuleId();
            Integer match = filterBundle.getMatch();
            Integer total = filterBundle.getTotal();


            ksFilterLog.setRuleId(ruleId);
            ksFilterLog.setFilePath(filter.getSaveFilePath());
            ksFilterLog.setFilterName(filter.getFilterName());
            ksFilterLog.setKsMatch(match);
            ksFilterLog.setKsNoMatch(total - match);
            ksFilterLog.setSourceType(isTask ? "自动执行" : "手动执行");
            ksFilterLog.setCreateTime(LocalDateTime.now());
            ksFilterLogRepository.save(ksFilterLog);

            KsFilterStatistics ksFilterStatistics = queryFactory.selectFrom(qKsFilterStatistics).where(qKsFilterStatistics.ruleId.eq(ruleId)).fetchFirst();

            Long clueResultTotal = queryFactory.select(qKsFilterResult.count()).from(qKsFilterResult).where(qKsFilterResult.ruleId.eq(filterParam.getKsRuleId())).fetchOne();
            if (ksFilterStatistics == null) {
                ksFilterStatistics = new KsFilterStatistics();
            }
            ksFilterStatistics.setFilterId(filterId);
            ksFilterStatistics.setRuleId(filterParam.getKsRuleId());
            ksFilterStatistics.setRunCount(ksFilterStatistics.getRunCount() + 1);
            ksFilterStatistics.setClueResultTotal(Objects.requireNonNull(clueResultTotal).intValue());
            ksFilterStatistics.setRunDataTotal(total);
            ksFilterStatistics.setRunLastTime(LocalDateTime.now());
            ksFilterStatisticsRepository.save(ksFilterStatistics);
        });

    }

}
